"""
这个模块中的函数为学习如何使用曲尺提供了一个很好的起点，并且介绍了它与其他编程环境的不同之处
"""
import numpy as np
from skimage import measure as _measure
from skimage.color import hsv2rgb as _hsv2rgb

NEG_Z = -1e-12
POS_Z = 1e-12

def complex_grid(x_min=-10, x_max=10, y_min=-10, y_max=10, num=21):
	"""
	创建一个由复数组成的相交线网格

参数
----------
x_min : float
    最小x坐标
x_max : float
    最大x坐标
y_min : float
	最小y坐标
y_max : float
    最大y坐标
num : int
	每个坐标轴网格线的数量

注意
-----
如果网格通过x轴或y轴，则此函数返回网格所占平面的每个象限的网格。当使用complex_grid作为ufunc的输入时，这一点是很重要的，因为它可以避免由于距离很近但符号不同而产生的视觉假象。

例子
--------

要创建一个复杂的网格，我们使用numpy的“meshgrid”方法，第一步是创建两个等距数字数组

>>> from numpy import linspace, meshgrid, transpose
>>> x = linspace(0,10)
>>> y = linspace(0,10)

然后，我们可以用下面的方式创建一组直线“Z”

>>> Y,X = meshgrid(y,x)
>>> Z = X + 1j*Y

为了画出网格，我们将“Z”转换为另一个方向的线

>>> plot(Z,transpose(Z))
    """
	if (x_min < 0 and x_max > 0) or (y_min < 0 and y_max > 0):	
		return (*complex_grid(x_min, NEG_Z, y_min, NEG_Z, int(num/2)),
			*complex_grid(POS_Z, x_max, y_min, NEG_Z, int(num/2)),
			*complex_grid(x_min, NEG_Z, POS_Z, y_max, int(num/2)),
			*complex_grid(POS_Z, x_max, POS_Z, y_max, int(num/2)))

	x = np.linspace(x_min, x_max, num+1)
	y = np.linspace(y_min, y_max, num+1)
	X, Y = np.meshgrid(x,y)
	Z = X + 1j*Y

	return Z, np.transpose(Z)

def power(points, exponent):
	"""
取任意指数的一组点的幂的结果

参数
----------
points : array
    一个numpy数组

exponent: float
	指数


例子
--------

--------

>>> from examples import complex_grid
>>> from numpy import power
>>> t=1
>>> plot(power(complex_grid(), 2))

动画:

>>> app.animate("power", argname="x2", start=2, stop=-2, step=-0.005, delay=0.01)

    """
	return points**exponent

def times_table(factor, start=0, end=199):
	"""
创建一个数除以因子f的余数的一个可视化表示，用圆上的点来表示这些数字

参数
----------
factor : float
    最小x坐标
start : int
    最大x坐标
end : int
	最小y坐标

注意
-----
如果网格通过x轴或y轴，则此函数返回网格所占平面的每个象限的网格。当使用complex_grid作为ufunc的输入时，这一点是很重要的，因为它可以避免由于距离很近但符号不同而产生的视觉假象。



例子
--------
>>> from examples import times_table
>>> t=2
>>> plot(times_table(t))

动画

>>> app.animate("t", start=2, stop=50)
    """

	result = []
	
	for value in range(start, end):
		new_value = (value * factor) % end
		result.append([
			(np.cos(value * 2 * np.pi / end), np.sin(value * 2 * np.pi / end)),
			(np.cos(new_value * 2 * np.pi / end), np.sin(new_value * 2 * np.pi / end))
		])

	return tuple(result);

def elliptic_curve(q,p):
	"""
画一条q,p作为参数的椭圆曲线

参数
----------
p : float
    x系数

q : float
    常数项

注意
-----
这个函数是通过使用skimage库来实现的，它可以找到满足方程y^2 = x^3 + p*x + q的(x,y)点集


通过代入，椭圆曲线的多项式形式与Weierstrass椭圆函数[1]所满足的微分方程有关

ro(z)′ = y

ro(z)  = x

参考
----------
[0] https://en.wikipedia.org/wiki/Elliptic_curve


[1] https://en.wikipedia.org/wiki/Weierstrass%27s_elliptic_functions#Differential_equation

例子
--------

>>> from examples import elliptic_curve
>>> plot(elliptic_curve(0,0))
    """
	x, y = np.ogrid[-12:12:100j, -12:12:100j]
	r = y**2 - x**3 - x*p - q

	return _measure.find_contours(r, 0)

def four_leaf_rose(q=4):
	"""
画出被称为“四叶玫瑰”的代数族

:math: (x^2 + y^2)^3 - q*(x^2)*(y^2) = 0

参数
----------
q : float
    交叉项系数

注意
-----
这个函数是通过使用skimage库来实现的，它可以找到满足方程(x²+ y²)^3 - q*(x²)*(y²)= 0的(x,y)点集

例子
--------

>>> from examples import four_leaf_rose
>>> plot(four_leaf_rose())
    """
	x, y = np.ogrid[-12:12:100j, -12:12:100j]
	r = (x**2 + y**2)**3 - q*(x**2)*(y**2)
	return _measure.find_contours(r, 0)


def transform(f, g, X, t):
	"""
动画两个函数之间的过渡

:math: w(f,g,X,t) = f(X)*(t-1) + g(X)*t

参数
----------
f : array
    第一个要混合的函数
g : array
    第二个要混合的函数
X : array
	函数f和g的取值范围
t : float
   一个介于0和1之间的数字，它决定了f和g之间的混合量


例子
--------
>>> from examples import transform
>>> from numpy import exp, sqrt, linspace
>>> transform(exp, sqrt, linspace(-3,3), 0.5)
[ 0.5        -0.69980788  0.62129323 -0.29568233 -0.14752521]

图解积分法:

>>> from examples import complex_grid
>>> t = 0.5
>>> plot(transform(exp, sqrt, complex_grid(), t))

动画过渡:

>>> app.animate("t", start=1, stop=0, step=-0.001)
    """
	return f(X)*(t-1) + g(X)*t

def complex_points(position, size, step):
	"""
使用numpy方法创建复数点矩阵

:math: (x^2 + y^2)^3 - q*(x^2)*(y^2) = 0

参数
----------
position : tuple
    点中心的(x,y)位置

size : tuple
    包含点集的正方形的(宽度，高度)

step : float
    点之间的间距

注意
-----
这个函数使用numpy的“range”和“meshgrid”函数来创建一组复杂点。查看此示例的源代码，以查看实现细节。

例子
--------

>>> from examples import complex_points
>>> complex_points((0,0),(2,2),0.5)
[[-1. +1.j  -1. +0.5j -1. +0.j  -1. -0.5j]
 [-0.5+1.j  -0.5+0.5j -0.5+0.j  -0.5-0.5j]
 [ 0. +1.j   0. +0.5j  0. +0.j   0. -0.5j]
 [ 0.5+1.j   0.5+0.5j  0.5+0.j   0.5-0.5j]]
    """
	x0, y0 = position
	w, h = size
	y = np.arange(y0 + h/2, y0 - h/2, -step)
	x = np.arange(x0 - w/2, x0 + w/2, step)
	Y, X = np.meshgrid(y,x)
	return X + 1j*Y

def domain_coloring(position, size, step, transform=None):
	"""
域着色是一种常用的可视化复值函数输出方法。

参数
----------
position : tuple
    像素中心的(x,y)位置

size : tuple
    包含像素集的正方形的(宽度，高度)

step : float
    相邻像素之间的距离

例子
--------
>>> from examples import domain_coloring
>>> plot(pixelmap=domain_coloring)

参考
----------
3blue1brown, Winding numbers and domain coloring, https://www.youtube.com/watch?v=b7FxPsqfkOY


https://en.wikipedia.org/wiki/Domain_coloring


	"""
	Z = complex_points(position, size, step)
	if transform:
		Z = transform(Z)
	H = np.angle(Z) % (2*np.pi)
	r = np.log2(1. + np.abs(Z))
	S = (1. + np.abs(np.sin(2. * np.pi * r))) / 2.
	V = (1. + np.abs(np.cos(2. * np.pi * r))) / 2.

	color_wheel = np.empty((Z.shape[0], Z.shape[1], 3), dtype=np.uint8)
	color_wheel[:, :, 0] = 255 * (H / H.max())
	color_wheel[:, :, 1] = 255 * S
	color_wheel[:, :, 2] = 255 * V

	return 255 * _hsv2rgb(color_wheel)

def mandelbrot_set(position, size, step, iterations=80):
	"""
Mandlebrot集合是最著名的分形之一，这个函数计算它的定义方程直到给定的迭代次数。

:math: $M = lim_{n \\to iterations} z_n < 2$ where $z_{n+1}=z_n^2 + z_0$

参数
----------
position : tuple
    像素中心的(x,y)位置

size : tuple
    包含像素集的正方形的(宽度，高度)

step : float
    相邻像素之间的距离

iterations : int
	退出计算前的迭代次数

参考
----------
https://en.wikipedia.org/wiki/Mandelbrot_set


https://tomroelandts.com/articles/how-to-compute-the-mandelbrot-set-using-numpy-array-operations

例子
--------
>>> from examples import mandelbrot_set
>>> plot(pixelmap=mandelbrot_set)

	"""
	C = complex_points(position, size, step)
	C_size = C.shape[0], C.shape[1]
	Z = np.zeros(C_size, dtype=complex)
	M = np.full(C_size, True, dtype=bool)

	for i in range(iterations):
		Z[M] = Z[M] * Z[M] + C[M]
		M[np.abs(Z) > 2] = False

	M = np.uint8(M)
	R = np.empty((C_size[0], C_size[1], 3), dtype=np.uint8)
	R[:,:,0] = 255 * M
	R[:,:,1] = 255 * M
	R[:,:,2] = 255 * M
	return R	

def helix(t, as_points=False):
	"""
绘制一个三维螺旋

:math: helix(t) = (np.cos(t), t, np.sin(t))

参数
----------
t : float
    曲线参数

例子
--------
>>> from examples import helix
>>> from numpy import linspace
>>> T = 10
>>> plot(helix(linspace(0,T)))

动画:

>>> app.animate("T", start=10, stop=25)
    """
	result = np.cos(t), t, np.sin(t)
	return result if as_points else list(result)

def wireframe(x,y,fn):
	"""
绘制由两个实变量f(x,y)函数给出的曲面的线框线

:math: wireframe(t) = (x, y, fn(x,y))

参数
----------
x  : array
	x的取值范围

y  : array
	y的取值范围

fn : callable
    两个实变量的实值函数

例子
--------
>>> from numpy import linspace
>>> from examples import wireframe
>>> X = linspace(0,10)
>>> Y = linspace(0,10)
>>> plot(wireframe(X,Y, lambda x,y: (x,y,x**2 + y**2)))
    """
	
	x_lines, y_lines = [], []
	for xval in x:
		temp = []
		for yval in y:
			temp.append(fn(xval,yval))
		x_lines.append(temp)

	for yval in y:
		temp = []
		for xval in x:
			temp.append(fn(xval,yval))
		y_lines.append(temp)

	return x_lines, y_lines

def surface(x,y,fn):
	"""
为一个由两个实变量f(x,y)函数给出的曲面绘制四边形

:math: surface(t) = (x, y, fn(x,y))

参数
----------
x  : array
	x的取值范围

y  : array
	y的取值范围

fn : callable
    两个实变量的实值函数

例子
--------
>>> from examples import surface
>>> X = linspace(0,10)
>>> Y = linspace(0,10)
>>> plot(surface(X,Y, lambda x,y: (x,y,x**2 + y**2)))
	"""
	
	return wireframe(x,y,fn)[1]

def cylinder(pos=(0,0,0),radius=1,num=25, as_wireframe=False):
	"""
在给定的位置以给定的半径和细分的数量绘制一个圆柱体

:math: f(t) = (cos(t), sin(t), r)

参数
----------
pos  : tuple
	圆柱体中心的位置

radius : float
	圆柱体半径

num : int
    细分的数量

参数
--------
>>> from examples import cylinder
>>> plot(cylinder())
    """
	result = wireframe(
		np.linspace(-radius,radius, num=num),
		np.linspace(0, 2*np.pi, num=num),
		lambda r,t: (pos[0] + np.cos(t), pos[1] + np.sin(t), pos[2] + r)
	)

	return result if as_wireframe else result[1]

def sphere(pos=(0,0,0),radius=1,num=12, as_wireframe=False):
	"""
在给定的位置以给定的半径和细分的数量绘制一个球体

:math: f(t) = (cos(t), sin(t), r)

参数
----------
pos  : tuple
	圆柱体中心的位置

radius : float
	圆柱体半径

num : int
    细分的数量

注意
-----
这个函数的实现方式存在一些已知的问题，这些问题与“plot”模块的OpenGLWindow如何计算法线有关。

例子
--------
>>> from examples import sphere
>>> plot(sphere())
    """
	result = wireframe(
		np.linspace(-np.pi,np.pi, num=num),
		np.linspace(-np.pi,np.pi, num=num),
		lambda p,t: (pos[0] + radius*np.cos(t)*np.cos(p), pos[1] + radius*np.cos(t)*np.sin(p), pos[2] + radius*np.sin(t))
	)
	return result if as_wireframe else result[1]

